package com.wetao.testkotlin

class ATest {
    /*
        范围表达式： if(a in 1..5){}
        "aaa, ${if(flag) "可以" else "不可以"}"
        函数没有返回类型，返回Unit
        函数报错，返回Nothing
        函数参数可以使用默认值，调用可以用参数的名字
        反引号函数名：fun `aa bb cc`() {}, 作用：测试函数使用，java和kotlin关键字不一样，或者某些名字被系统使用了，可以用反引号的方式。调用： `aa bb cc`();
        匿名函数：val noNameFun:()->String  这是一个匿名函数的声明，这个变量是一个函数，没有参数，返回值是String
                noNameFun = {"hello fun"}  给这个函数变量复制一个函数，没有参数，返回 "hello fun", 返回最后一句的结果, 调用：noNameFun()
                val noNameFun2: (String)->String = {name -> "hello fun2"}   调用：noNameFun2("jack")
                val noNameFun3: (String)->String = {"hello ${it}"} 调用：noNameFun3("tom") 如果只有一个参数事，可以用it默认代替这个参数
                val noNameFun4 = {"hello fun4"} 不显示指定类型，类型推断出函数类型：()->String， 调用：noNameFun4()
                val noNameFun5 = {name:String, year:Int ->
                    val s = "$name 年龄是 $year"
                    "$s 好的"
                }   有参数要给类型，才能推断出函数类型的参数类型, 调用：noNameFun5("zhansan", 16)
                val noNameFun6: (String, Int)->String = {name, year ->
                    val s = "$name 年龄是 $year"
                    "$s 好的"
                } 调用： noNameFun6("lisi", 17)
                匿名函数就是lambda表达式，返回数据就是lambda结果
        向具名函数中传匿名函数作为参数
             val noNameFun: (String, Int) -> String = {name, age - >
                val a = "hello"
                "$a -- $name -- $age"
             } //声明匿名函数
             fun nameFun(name: String, noNameFunParam: (String, Int) -> String) {
                   noNameFunParam(name, 16)  //调用匿名函数，在函数里面要调用匿名函数，参数一般来自具名函数的参数
              } //声明具名函数
              nameFun("tome", noNameFun) //调用具名函数

              将上面改成简略写法如下：
                  fun nameFun(name: String, noNameFunParam: (String, Int) -> String) {
                      noNameFunParam(name, 16)  //调用匿名函数，在函数里面要调用匿名函数，参数一般来自具名函数的参数
                  } //声明具名函数
                  nameFun("tome") { name: String, age: Int -> //声明匿名函数，如果最后一个参数是匿名函数，可以放到括号外面
                        val a = "hello"
                        "$a -- $name -- $age"
                  } //调用具名函数
               上面使用的是两个参数，第二个参数是匿名函数的情况，如果调用的函数只有一个参数，这个参数是你们函数，可以怎么简化呢？比如字符串有一个这样的函数调用，如下：
                   “abcdefg”.count({it == "a"}) //只有一个参数，参数是一个函数，用大括号括起来
                   “abcdefg”.count{it == "a"}  //只有一个参数，参数是一个函数，用大括号括起来,并且去除小括号
         内联函数：在函数前加inline, 因为lambda表达式会产生内存开销，加上inline可以解决这个问题，表示函数中的内容会替换到调用的地方去。

         函数引用：具名函数中参数使用具名函数：
             fun nameFun(name: String, quoteFunParam: (String, Int) -> String) {
                   quoteFunParam(name, 16)
              }
              fun quoteFun(name: String, age: Int): String{
                    val a = "hello"
                    return "$a -- $name -- $age"
              }
              nameFun("zhansan", ::quoteFun) //调用函数引用

        函数返回函数类型
            fun nameFun(): (String) -> String {
                val c = "123"
                return { name: String ->
                    val a = "abc"
                    "$a --- $name---$c"  //这里有闭包效果，即可以拿到$c的值
                }
            }
            val f: (String)->String = nameFun()  //返回的是一个函数
            f("lisi")  //调用，返回的是一个字符串

        kotlin里面函数参数里面是函数，而java没有这个功能，java是函数参数是一个接口，我们新建一个匿名的接口对象来实现一样的功能。所以kotlin里面直接传函数的方式更方便。

        根null有关的： ?. !! ?:

        异常：
           自定义异常：
               class UnskilledException() : IllegalArgumentException(“出错了”)
               fun f(number: Int?) {
                    number ?: throw UnskilledException()
               }
               try{f(null)}catch(e: Exception) {}


        字符串操作：
            “aaabbbccc”.substring(0, 2) 或 “aaabbbccc”.substring(0 until 2)
            val str = "abcd".replace(Regex("[ab]")) {
                when(it.value) {
                    "a" -> "8"
                    "b" -> "9"
                    else -> it.value
                }
            }  得到的结果是：89cd
            == === 如果a = "ab"  b = “ab” 则：a===b 返回true， 因为字符串有一个字符串常量池，代表同一个对象
            “abc”.forEach{print(it)}
            "8".toInt()  "9".toIntOrNull() //如果含小数点，转换会是null

          数值类型：
             8.1.toInt()
             8.1.roundToInt()
             "%.2f".format(8.123) 返回字符串

         let、also 里面用it, let返回最后一行，also返回自己
         also、apply 都返回自己， apply的里面是this
         run、with 里面用this,返回是最后一行，with传参要用小括号
         takeIf: 需要判断lambda中提供的条件表达式，如果是true,返回接收对象，如果是false,则返回null
                File("a.txt").takeIf{it.exists() && it.canRead}?.readText() //表示满足条件才读文件中的文本
         takeUnless 跟takeIf刚好相反，很少用

         let用于对空的处理和设置局部域
         apply用于对象配置
         also额外的效果
         run对象配置并计算结果
         with在一个对象上组合函数调用

         集合：
            val l = listOf("aaa", "bbb")
            l.getOrElse(3){"超过索引"}
            l.getOrNull(3)
            val ml = mutableListOf("ccc", "ddd")
            l.toMutableList()
            ml.toList()
            ml += "eee"
            ml -= "eee"
            ml.removeIf{it.contains("eee")}
            for(s in l){}
            l.forEach{println(it)}
            l.forEachIndexed{index, item -> {}}
            val (_:String, second: String) = l  //解构赋值，如果不需要的值，用下划线
            val s = setOf("aa", "bb")
            s.elementAt(0)
            l.toSet().toList() //去重
            l.distinct() //去重，跟上面的效果一样
            数组： IntArray - intArrayOf(),FloatArray - floatArrayOf()等等，对象数组用：Array - arrayOf()
            val m = mapOf("a" to 1, "b" to 2)
            val m2 = mapOf(Pair("a", 1), Pair("b", 2))
            m["a"]
            m.getValue("a")
            m.getOrElse("a"){"错误"}
            m.getOrDefault("a", 0)
            m.forEach{
                println("${it.key}, ${it.value}")
            }
            m.forEach{ (key: String, value: Int) ->
                println("$key, $value")
            }
            val mm = mutableMapOf()
            mm += "c" to 3
            mm.put("d", 4)
            mm.getOrPut("d") {4} 获取失败，就put

        类：
          class Player{
            var name:String = "jack"
                get() = field.capitalize()
                set(value) {
                    field = value.trim()
                }
          }
          如果主构造函数中变量没有var或val,表示变量不能在函数里面使用，可以var name = _name 赋值给成员变量
          次构造函数必须调用主构造函数： constructor(name: String): this(name) {}
          初始化块，只能放一个，用于赋值，和用于检查构造函数传过来的值是否有效,应该放到所有属性的下面，否则有问题
           init{
           }
           初始化顺序：主构造函数声明的属性-》类级别的属性赋值-》init初始化块属性赋值-》次构造函数里面的属性赋值
          lateinit 延迟初始化， 通过isInitialized检查是否初始化了
          惰性初始化：
             val name by lazy {getName()}
             fun getName():String {return "zhansan"}
          继承 类名字前，属性前，函数前 都要加open
               a is Player  类型检查
               a as Player 类型转换， 转换了一次，下面再有就不需要再转，编译器能够知道
               Any 是所有类的超类
          Object的三种使用方式: 对象声明，对象表达式，伴生对象
              对象声明也叫单例对象：
                  object ObjConfig{
                    doSomething(){}
                  }
                  ObjConfig.doSomething()  //单例的调用
              对象表达式： 常用于只需要用一次的类
                  item.onClick(object: {})
              伴生对象，类似静态属性和静态方法
                 class MyConfig{
                    companion object{
                        const val PATH = "aa.txt"
                        fun load() {}
                    }
                 }
                 MyConfig.load()

           嵌套类：
               class Play{
                    class Play2{
                    }
               }
               val p = Play.Play2()  //类似一个单独的类

          内部类：
               class Outer {
                    private val bar: Int = 1
                    inner class Inner {
                        fun foo() = bar
                    }
                }
                val demo = Outer().Inner().foo()

         数据类：
            data class MyData(val x, val y){}
            也可以用==比较内容 和===比较引用
            有copy函数 val c = s.copy(name="chen"), 只会复制主构造函数的属性
            有解构声明：component1、component2 ..., 实现了component函数的类才能用解构赋值的语法
                val (x, y) = MyData(1, 2)

         运算符重置：
             operator fun plus(a: Player) = Player(x + a.x)
             + ==》 plus
             += ==》 plusAssign
             == ==》 equals
             > ==》 compareTo
             [] ==》 get
             .. ==》 rangeTo
             in ==》 contains

         枚举类：
            enum class MyE{
                MY_A,
                MY_B
            }

         密封类： 常在when中使用 is My1, 不需要else
            sealed class MySealed {
                object My1: MySealed()
                object My2: MySealed()
            }

        接口：
           interface MyI{}

        抽象类：
           abstract class MyA() {
                abstract fun getA():String
           }

       泛型类：
          class MyB<T>(item:T) {
                val a: T = item
          }

       泛型函数：
          fun <R> getC(): (R)->R {}

       泛型约束：
          class MyD<T: Human>(item: T) {}  //T只能传Human或者其子类

       关键字 vararg 类似java的...
          fun setF(vararg item: Int) {}

       out/in常用于集合类型的泛型
           class MyG<T: Human>(vararg item:T) {
                var myItem: Array<out T> = item
           }

       扩展函数: 实际上类似java的成员函数，里面传了一个this参数
            省略，看官网
            泛型扩展函数：
               fun <T> T.easyPrint(): T{
                    println(this)
                    return this
               }
               使用： “abc”.easyPrint()
                      15.easyPrint()
                      很多是这样做的的，比如let
           扩展属性：
              val String.numVowels
                    get() = count{"abcd".contains(it)}
           可空类型的扩展
               fun String?.printWithDefault(default:String)  = print(this?:default)
               使用： val nullData: String? = null
                     nullData.printWithDefault("空数据")


       infix关键字
            infix fun String.aF(b: String){}
            "aa" aF "bb"   //加了infix就可以用这种写法，相当于： “aa”.aF("bb")

      kotlin和java的互操作性：
          @JvmName
              在kotlin文件的类外面定义的函数，java文件调用它的方式：文件名Kt.函数名()  ,必须加上Kt
              在Kotlin文件顶部加上：@file:JvmName("Aaa"), 则java文件调用它的方式： Aaa.函数名()

          @JvmField
              kotlin类中一个属性 spell, java中调用：对象.getSpell();
              Kotlin属性spell上面加上@JvmField, 则java中调用：对象.spell;

          @JvmOverloads
              kotlin类中的函数有默认参数 fun hF(a: String="aa", b: String = "bb"), java中调用：对象.hF("AA", "bb")只需要一个参数事也要将两个参数写上。
              kotlin类在hF函数上加上@JvmOverloads, 则java中调用：对象.hF("AA"), 只需要写一个参数

          @JvmStatic
              kotlin类中companion object {val AA:String = "aa"}， java中调用：类名.Companion.getAA();
              kotlin类在AA上面加上@JvmStatic, 则java中调用：类名.AA

          @Throws
             java函数抛出的异常，java调用这个函数的地方编译器会提示要try--catch处理， 但是kotlin调用java的这个抛异常的函数，编译器不会提示要try--catch处理，但是我们可以使用try--catch处理
             因为kotlin异常处理是自由的，是否处理是有用户自己决定，这个跟java不一样。
             如果kotlin某个函数抛出异常，而在java中编译器也没用提示处理异常，并且我们try--catch写IOException会报错, 只能写Throwable在catch里面才行
             所以我们要做kotlin抛异常的函数上面加： @throws(IOException::class), 这样java中的catch才能写IOException

          函数类型操作：
             如果kotlin一个属性是一个函数类型val f : (String) -> String, 那在java中怎么使用呢？
             Function1<String, String> f = 对象.f(); 1表示一个参数， 第一个String表示参数是String, 第二个String表示返回值是String, 如果有两个参数则Function2, 可以0-22，总共23个函数
             f.invoke(); //执行这个函数











    * */
}